#ifndef INVERTED_INDEX_H
#define INVERTED_INDEX_H

#include "Graph.h"
#include <string>
#include <vector>
#include <time.h> 
#include <iostream>
#include <stdio.h>
#include <set>
#include <list>
#include <unordered_map>

#define TIME_INTERVAL_SIZE 1000 // each inverted list, number of tweets in a time slice


struct ForwardList{
	int doc_id;			// the tweet id of this entry
	int user_id;		// user's id and this user publish the tweet
	time_t doc_time;	// time when this tweet is published
	unordered_map<int,double> keyword_id_tf;// map between keyword id and term frequency

	void print(){
		printf("%10d|%8d|%15ld|",doc_id,user_id,doc_time);
		for(auto it = keyword_id_tf.begin(); it != keyword_id_tf.end(); it++){
			printf("(%d,%.3f) ",it->first,it->second);
		}
	};
};

struct InvertedListEnt{
	ForwardList* doc; // document reference
	double tf; // distance to pivot

	InvertedListEnt(ForwardList* _doc, double _tf){
		doc = _doc;
		tf = _tf;
	};
};

struct InvertedListEntSortedFreq {
    bool operator() (const InvertedListEnt& lhs, const InvertedListEnt& rhs) const{
		if(lhs.tf > rhs.tf) return true;
		else if(lhs.tf < rhs.tf) return false;
		return lhs.doc->doc_id < rhs.doc->doc_id;
    }
};

typedef list<InvertedListEnt> Container3D;

struct InvertedList{
	// 3 D inverted index
	// first dimension is the time
	// second dimension is the social cluster
	// third dimension is the frequency
	// each entry is a set of documents
	list<Container3D**> inv3dindex;
	vector<list<Container3D**>::iterator > slice_iter_vec;

	// the first dimension discretized value, each entry represent the nearest time
	vector<time_t> time_interval; 

	vector<int> min_dist_to_partition_pivot;	// distance from user to the partition pivot: min
	vector<int> max_dist_to_partition_pivot;	// distance from user to the partition pivot: max
	vector<bool> frequency_empty;				// check whether this keyword has doc in this frequency range
	
	int list_count; // number of document in the inverted list

	~InvertedList()
	{
		list_count = 0;
		time_interval.clear();
		slice_iter_vec.clear();
		max_dist_to_partition_pivot.clear();
		min_dist_to_partition_pivot.clear();
	}
};




// entry to store the top K results
struct ResultEnt{
	int doc_id;			// tweet id in this case
	double rank_value;  // ranking value of the results;
	
	bool operator<(const ResultEnt& another) const
	{
		return rank_value > another.rank_value;
	}
};

struct ContainerEnt{
	int time_dim;
	int social_dim;
	int freq_dim;

	Container3D* container;
	double best_value;

	ContainerEnt(int i, int j, int k, Container3D* ent){
		time_dim = i;
		social_dim = j;
		freq_dim = k;
		container = ent;
	}

	ContainerEnt(){
		time_dim = -1;
		social_dim = -1;
		freq_dim = -1;
		container = NULL;
		best_value = -1;
	}

	bool operator<(const ContainerEnt& another) const
	{
		return best_value < another.best_value;
	}
};


class InvertedIndex{
public:
	InvertedIndex(string in_dir,int time_partition,Graph* graph);
	~InvertedIndex();
	int GetFreqInterval(double frequency);	// get the frequency interval index given a frequency

	// for the 3D grid
	void SingleKeywordQuery(int keyword_idx, int user_idx, int topK, int op_level);
	void MultiKeywordQuery(vector<int> keyword_idx, int user_idx, int topK, int op_level);

	int GetNumKeywords(){return inverted_lists.size();};
	int GetKeywordInvertedListSize(int keyword_idx){return inverted_lists[keyword_idx].list_count;};
	int GetKeywordIdx(string keyword){return keyword_map[keyword];};

	// for baseline inverted list
	void SingleKeywordQueryBaseLineInvertedIndex(int keyword_idx, int user_idx, int topK, int op_level);
	void MultiKeywordNaviveBaseLineInvertedIndex(vector<int> keyword_idxes, int user_idx,int topK, int op_level);
	
	void MultiKeywordNormalBaseLineInvertedIndex(vector<int> keyword_idxes, int user_idx, int topK, int op_level);

	// temp: shoud be private
	vector<InvertedList> inverted_lists;			// each keyword keep a inverted_list
	vector<list<InvertedListEnt> > base_line_inverted_lists; // inverted list sorted by time
	vector<set<InvertedListEnt,InvertedListEntSortedFreq> > normal_inverted_lists;

	// for naive checking
	void SingleKeywordNavive(int keyword_idx, int user_idx, int topK,  int op_level);
	void MultiKeywordNavive(vector<int> keyword_idx, int user_idx,int topK,  int op_level);

	// set linear coefficient
	void SetParameter(double a, double b, double g){alpha = a; beta = b; gamma = g;};

	// sample a random keyword, keyword_freq_degree = 0,1,2 : low mid high
	vector<int> SampleKeyword(int keyword_freq_degree);

	double alpha;
	double beta;
	double gamma;

	void TestSuit(int time_slice_size);

private:
	// class parameters
	string project_dir;			// project directory
	int time_partition_id;
	Graph* social_graph;		// social graph
	int number_docs;			// number of tweets or doc

	// post lists: load after forward lists
	void LoadInvertedLists(int time_slice_size);	// a time slice can contain time_interval_size number of tweets
	void ClearInvertedLists();

	// forward lists
	vector<ForwardList> forward_lists;	// store forward list info
	void LoadForwardLists();			// load the forward list into forward_lists
	
	// keyword information
	unordered_map<string,int> keyword_map;	// map between keyword to the keyword index
	vector<double> keyword_idf;				// each entry denotes how many of documents contain this keyword

	// frequency dimension information
	void LoadFreqInterval();
	vector<double> freq_interval_val;

	// create a new slice
	void CreateNewSlice(InvertedList& list4insert);

	time_t oldest_tweet_time;
	time_t latest_tweet_time;

	// rank the keyword from low to high with respect to appearances in the inverted list
	// the first element represents keyword inverted list length and 
	// the second element represents keyword idx
	vector<pair<int,int> > keyword_idf_rank; 

	// estimate the single keyword query's container;
	void SingleKeywordEstimateContainer(ContainerEnt& container_ent,InvertedList& searching_list, 
		time_t& max_time_diff, vector<pair<int,int> >& social_sort_vec);

	void MultiKeywordEstimateContainer(ContainerEnt& container_ent, InvertedList& searching_list, double keyword_idf, double idf_sum_cons,
		time_t& max_time_diff, vector<pair<int,int> >& social_order_vec, vector<pair<double,int > >& freq_order_vec, int num_query_keywords);

	// load after the forward lists
	void LoadBaselineInvertedLists();

	// load after the forward lists, sorted by term frequency
	void LoadNormalInvertedLists();

	double GetRankingSocre(double time_score, double social_score, double text_score){return alpha*time_score+beta*social_score+gamma*text_score;};
	double GetDistThreshold(double currentTopK, double time_score, double text_score){return (currentTopK-alpha*time_score-gamma*text_score)/beta;};
};




#endif